/*
 * OrderFacadeBean.java
 *
 * Created on 8 May 2007, 12:09
 * Copyright (C) 2007   Yuezhong Zhang
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package usyd.comp5348;

import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.ws.WebServiceRef;

/**
 *
 * @author Yuezhong Zhang
 * @author  David
 */
@Stateless(mappedName="ejb/Order")
public class OrderFacadeBean implements OrderFacadeRemote, OrderFacadeLocal {
    
    @EJB
    private DeliveryFacadeRemote delivery;
    //above delivery is used for RPC call to submit delivery
    //but for assignment 2, this isn't in use'
//
//    @Resource(mappedName = "jms/DeliveryMessageDrivenBeanFactory")
//    private ConnectionFactory deliveryMessageDrivenBeanFactory;
//
//    @Resource(mappedName = "jms/DeliveryMessageDrivenBean")
//    private Queue deliveryMessageDrivenBean;
    
    @WebServiceRef(wsdlLocation = "http://localhost:8080/CreditCardService/CardValidationServiceService?wsdl")
    private usyd.comp5348.CardValidationServiceService service;
    
    @PersistenceContext
    private EntityManager em;
    
    private Logger logger = Logger.getLogger("usyd.comp5438.OrderFacadeBean");
    
    @EJB(mappedName="ejb/Catalogue")
    private CatalogueFacadeRemote catalogueFacadeBean;
    
    @EJB(mappedName="ejb/Customer")
    private CustomerFacadeRemote customerFacadeBean;
    
    /** Creates a new instance of OrderFacadeBean */
    public OrderFacadeBean() {
    }
    
    public int createOrder(Order ord) throws OrderException{
        validateOrder(ord);
        float cost = calculateOrderCost(ord);
        ord.setTotal(cost);
        em.persist(ord);
        em.flush();
        return ord.getId();
    }
    
    public void modifyOrder(Order ord) throws OrderException{
        Order order = em.find(Order.class,ord.getId());
        if(order == null) {
            OrderException oex  = new OrderException("Couldn't find order with id="+ ord.getId());
            logger.warning(oex.getMessage());
            throw oex;
        }
        order.setCustomerId(ord.getCustomerId());
        order.setOrderDate(ord.getOrderDate());
        order.setStatus(ord.getStatus());
        order.setStore(ord.getStore());
        order.setWarehouse(ord.getWarehouse());
        //we clear it first, then update it with new info
        for(OrderLine ol : order.getVideos()){
            order.getVideos().remove(ol);
            em.remove(ol);
            em.flush();
        }
        em.merge(order);
        em.flush();
        for(OrderLine ol : ord.getVideos()){
            OrderLine line = new OrderLine();
            line.setCatalogueId(ol.getCatalogueId());
            line.setOrderId(order);
            line.setQuantity(ol.getQuantity());
            em.persist(line);
            em.flush();
            order.getVideos().add(line);
        }
        em.merge(order);
        em.flush();
    }
    
    public void deleteOrder(int id) {
        Order ord = getOrderById(id);
        if(ord !=null){
            em.remove(ord);
            em.flush();
        }
    }
    public void submitOrder(int id) throws OrderException {
        Logger.getLogger(getClass().getName()).log(Level.INFO,"try to submit order");
        Order ord =  getOrderById(id);
        if(ord == null){
            throw new OrderException("Specified Order doesn't exisit");
        }
        Logger.getLogger(getClass().getName()).log(Level.INFO,"validating customer id");
        Customer cust = customerFacadeBean.getCustomerById(ord.getCustomerId());
        Logger.getLogger(getClass().getName()).log(Level.INFO,"validating credit card");
        CardValidationService port = service.getCardValidationServicePort();
        // TODO initialize WS operation arguments here
        String cardNo = cust.getCardNumber();
        String name =  cust.getName();
        String expiryDate = cust.getCardExpireDate();
        // TODO process result here
        boolean isValid = port.validateCcard(cardNo, name, expiryDate);
//        boolean isValid = true;
        if(! isValid){
            throw new OrderException("Invalid credit card detail,failed to validate");
        }
        Logger.getLogger(getClass().getName()).log(Level.INFO,"credit card is valid");
        try {
            Logger.getLogger(getClass().getName()).log(Level.INFO,"send JMS message to delivery");
            sendJMSMessageToDeliveryMessageDrivenBean(ord);
        } catch (JMSException ex) {
            ex.printStackTrace();
            throw new OrderException(ex.getMessage());
        } catch (NamingException ex) {
            ex.printStackTrace();
            throw new OrderException(ex.getMessage());
        }
    }
    
    public Order getOrderById(int id) {
        return em.find(Order.class,id);
    }
    
    public void updateOrderStatus(int id, int newStatus)throws OrderException {
        //TODO implement updateOrderStatus
        Order ord = getOrderById(id);
        if(ord == null){
            OrderException oe = new OrderException("Order doesn't exist");
            logger.warning(oe.getMessage());
            throw oe;
        }
        ord.setStatus(newStatus);
        em.merge(ord);
        em.flush();
    }
    
    public float calculateOrderCost(Order ord) {
        //TODO implement calculateOrderCost
        float orderCost = 0.0f;
        Catalogue cat=null;
        for(OrderLine ol : ord.getVideos()){
            cat = catalogueFacadeBean.getVideoById(ol.getCatalogueId());
            orderCost += cat.getPrice();
        }
        return orderCost;
    }
    
    public void validateOrder(Order ord) throws OrderException {
        //TODO implement validateOrder
        OrderException oex;
        if(ord.getStatus() != Order.PENDING){
            oex = new  OrderException("Order already submitted");
            logger.warning(oex.getMessage());
            throw oex;
        }
        Customer cust =  customerFacadeBean.getCustomerById(ord.getCustomerId());
        if(cust == null){
            oex = new OrderException("Customer not found");
            logger.warning(oex.getMessage());
            throw oex;
        }
        Catalogue cat=null;
        for(OrderLine ol : ord.getVideos()){
            cat = catalogueFacadeBean.getVideoById(ol.getCatalogueId());
            if(cat == null){
                oex = new OrderException("Video #" + ol.getCatalogueId() + " not found in catalogue");
                logger.warning(oex.getMessage());
                throw oex;
            }
        }
    }
    
    private Message createJMSMessageForDeliveryMessageDrivenBean(Session session, Order order) throws JMSException {
        ObjectMessage tm = session.createObjectMessage();
        tm.setObject(order);
        return tm;
    }
    
    private void sendJMSMessageToDeliveryMessageDrivenBean(Order order) throws NamingException, JMSException {
        Connection connection = null;
        Session session = null;
        try {
//
//            connection = deliveryMessageDrivenBeanFactory.createConnection();
//            session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//            MessageProducer messageProducer = session.createProducer(deliveryMessageDrivenBean);
//             //Message message = session.createTextMessage("testing");
//            messageProducer.send(message);
            /** we look up the connection from lookupsevice, (which has a warehousename
             * and ip address mapping table,  so that it support multiple warehouse and shops.
             */
            RemoteDeliveryQueueLookupService service  = RemoteDeliveryQueueLookupService.getInstance();
            ConnectionFactory cf = service.lookupConnectionFactory(order.getWarehouse());
            connection = cf.createConnection();
            session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
            Queue dest = service.getQueue(order.getWarehouse());
            MessageProducer messageProducer = session.createProducer(dest);
            messageProducer.send(createJMSMessageForDeliveryMessageDrivenBean(session, order));
        } catch (Exception e) {
            e.printStackTrace();
            throw new JMSException(e.getMessage());
        } finally {
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }
    /**
     * submit the order to delivery to RPC call.. 
     * the same thing in assignemnt 1 
     *
     */
    public void submitOrder(Order order) throws OrderException {
        try {
            Logger.getLogger(getClass().getName()).log(Level.INFO,"try to submit order");
            Order ord =  getOrderById(order.getId());
            if(ord == null){
                throw new OrderException("Specified Order doesn't exisit");
            }
            Delivery d = new Delivery();
            d.setDeliveryDate(new Date());
            d.setOrderId(ord.getId());
            d.setStatus(Order.PENDING);
            d.setWarehouse(ord.getWarehouse());
            delivery.createDelivery(d);
            delivery.submitDelivery(d,ord);
        } catch (OrderException ex) {
            ex.printStackTrace();
            throw ex;
        } catch (DeliveryException ex) {
            ex.printStackTrace();
            throw new OrderException(ex.getMessage(),ex);
        }
    }
    
}
